﻿#region Copyright(C) 2018 FI Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2018 FI Ltd. All rights reserved.
//
// SYSTEM NAME      : FI.Net Application Framework
// COMPONENT ID     : ApplicationContext
// COMPONENT DESC   : Session based contextual data sharing.
//
// CREATED DATE/BY  : 18 Aug 2018 / Tian Mi
//
// REVISION HISTORY :
// DATE/BY            SR#/CS/PM#/OTHERS      DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using FI.Core.ApplicationContexts.Configuration;
using FI.Core.Configuration;
using FI.Core.Utilities;
using System.Collections.Generic;
using System.Configuration;
using System.Web;
using System.Web.SessionState;

namespace FI.Core.ApplicationContexts
{
    /// <summary>
    /// The concrete <see cref="ContextLocatorBase"/> which use the <see cref="HttpSessionState"/> as the context storage.
    /// </summary>
    [ConfigurationElement(typeof(HttpSessionStateContextLocatorData))]
    public class HttpSessionStateContextLocator : ContextLocatorBase
    {
        #region Private Members
        private const string SessionKeyOfContextItemKeys = "FI.ApplicationContexts.SessionKeyOfContextItemKeys";
        private IList<string> ContextItemKeys
        {
            get
            {
                if (null == HttpContext.Current.Session[SessionKeyOfContextItemKeys])
                {
                    HttpContext.Current.Session[SessionKeyOfContextItemKeys] = new List<string>();
                }
                return (IList<string>)HttpContext.Current.Session[SessionKeyOfContextItemKeys];
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// define the backup locator
        /// </summary>
        public CallContextLocator CallContextLocator { get; private set; }

        /// <summary>
        /// get the session state's availabe
        /// </summary>
        public bool SessionStateAvailable 
        {
            get
            {
                return null != HttpContext.Current && null != HttpContext.Current.Session;                
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="HttpSessionStateContextLocator" /> class.
        /// </summary>
        public HttpSessionStateContextLocator()
        {            
            this.CallContextLocator = new CallContextLocator();
        }
        #endregion

        #region Implement ContextLocator

        /// <summary>
        /// Get an existing context item by given key.
        /// </summary>
        /// <param name="key">The key of the <see cref="ContextItem"/> to get.</param>
        /// <returns>
        /// The <see cref="ContextItem"/> object to get.
        /// </returns>
        public override ContextItem GetContextItem(string key)
        {
            Guard.ArgumentNotNullOrEmpty(key, "key");
            if (this.SessionStateAvailable)
            {
                return HttpContext.Current.Session[key] as ContextItem;
            }
            else
            {
                return this.CallContextLocator.GetContextItem(key);
            }
        }

        /// <summary>
        /// Add a new context item or use the new context item to override the exiting one.
        /// </summary>
        /// <param name="contextItem">The new <see cref="ContextItem"/> to set.</param>
        protected override void SetContextItemCore(ContextItem contextItem)
        {
            Guard.ArgumentNotNull(contextItem, "contextItem");
            if (SessionStateAvailable)
            {
                HttpContext.Current.Session[contextItem.Key] = contextItem;
                if (!this.ContextItemKeys.Contains(contextItem.Key))
                {
                    this.ContextItemKeys.Add(contextItem.Key);
                }
            }
            else
            {
                this.CallContextLocator.SetContextItem(contextItem);
            }            
        }

        /// <summary>
        /// Get all current context item collection.
        /// </summary>
        /// <returns>
        /// A <see cref="ContextItemCollection"/> containg all of the current context items.
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists")]
        public override ContextItemCollection GetCurrentContext()
        {
            if (SessionStateAvailable)
            {
                ContextItemCollection context = new ContextItemCollection();
                foreach (var key in this.ContextItemKeys)
                { 
                    context.Add(this.GetContextItem(key));
                }
                return context;
            }

            return this.CallContextLocator.GetCurrentContext();
        }


        /// <summary>
        /// Clear the current context item collection.
        /// </summary>
        public override void Clear()
        {
            if (SessionStateAvailable)
            {
                foreach (var key in this.ContextItemKeys)
                {
                    HttpContext.Current.Session.Remove(key);
                }
                this.ContextItemKeys.Clear();
            }

            this.CallContextLocator.Clear();
        }


        /// <summary>
        /// Check if the context item of the given key exists.
        /// </summary>
        /// <param name="key">The key of the <see cref="ContextItem"/>.</param>
        /// <returns>
        /// true if the <see cref="ContextItem"/> already exists; otherwise, false.
        /// </returns>
        public override bool ContextItemExits(string key)
        {
            Guard.ArgumentNotNullOrEmpty(key, "key");
            if (SessionStateAvailable)
            {
                return null !=HttpContext.Current.Session[key];
            }

            return this.CallContextLocator.ContextItemExits(key);
        }

        #endregion
    }
}
